#pragma once

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

enum 
{
    SockError = 2,
    BindError,
    ListenErrror
};

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;


Log lg;

class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), tsvr(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

class TcpServer
{
public:
    TcpServer(int port, std::string ip = defaultip):listen_socket_(defaultfd), port_(port), ip_(ip)
    {}
    ~TcpServer()
    {}
    void InitServer()
    {
        listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
        if(listen_socket_ < 0)//创建失败处理
        {
            lg(Fatal, "socket error, errno : %d , errstr : %s", errno, strerror(errno));
            exit(SockError);
        }
        int opt = 1;
        setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));//防止偶发性的服务器无法立即重启

        lg(Info, "listen_socket_ success, sockfd : %d" , listen_socket_);//打印日志
        struct sockaddr_in local; //创建并填充套接字字段
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), (struct in_addr *)&local.sin_addr);

        //绑定
        if(bind(listen_socket_, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno : %d , errstr : %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind success, listen_socket_ : %d" , listen_socket_);//打印日志
        //监听
        if(listen(listen_socket_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno : %d , errstr : %s", errno, strerror(errno));
            exit(ListenErrror);
        }
        lg(Info, "listen success, listen_socket_ : %d" , listen_socket_);//打印日志
    }

    // static void* Routine(void* args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData* td = static_cast<ThreadData*> (args);
    //     td->tsvr->service(td->sockfd, td->clientip, td->clientport);
    //     delete td;
    //     return nullptr;
    // }

    void Start()
    {
        Daemon();
        //signal(SIGPIPE, SIG_IGN); // 避免向已经关闭描述符写入导致的错误
        lg(Info,"server is running");
        ThreadPool<Task>::GetInstance()->Start();
        for(;;)
        {
            //1、获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listen_socket_, (struct sockaddr *)&client, &len);
            if(sockfd < 0)
            {
                lg(Warning, "accept error, errno : %d , errstr : %s", errno, strerror(errno));
            }
            //获取客户端信息
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            //2、根据新连接进行通信
            lg(Info, "get a new link ... , listen_socket_:%d, client ip :%s, client port :%d",listen_socket_, clientip, clientport);

            //v1--单进程版本
            // service(sockfd, clientip, clientport);
            // close(sockfd);

            // //v2---多进程 
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //child
            //     close(listen_socket_); //这里由父进程来管理该描述符即可。
            //     if(fork() > 0) exit(0); // 将子进程退出，孙子进程将被操作系统领养。孙子进程退出会被系统回收。
            //     service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0); 
            // }
            // close(sockfd);//子进程已经拿到了，避免过多文件描述符导致父进程可用描述符变少

            // //father
            // pid_t rid = waitpid(id, nullptr, 0);//0 表示阻塞等待
            
            // //v3--多线程版本
            // ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            //v4--线程池版本
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
 
        }   

    }
    // void service(int sockfd, const std::string& clientip, const uint16_t &clientport)
    // {
    //     char buffer[4096];
    //     while(true)
    //     {
    //         ssize_t n = read(sockfd, buffer,sizeof(buffer));
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client say# " << buffer << std::endl;
    //             std::string echo_str = "tcp_server say# ";
    //             echo_str += buffer;
    //             write(sockfd, echo_str.c_str(),echo_str.size());
    //         }
    //         else if(n == 0)
    //         {
    //             lg(Info,"%s,%d quit, server close sockfd : %d",clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             lg(Warning, "read error , socket_:%d, client ip :%s, client port :%d",sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }
private:
    int listen_socket_;
    uint16_t port_;
    std::string ip_;
};